

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>webgl</title>
</head>
<body>
<script>
  var copyVideo = false;
  const setupVideo = (url) => {
    const video = document.createElement('video');

    var playing = false;
    var timeupdate = false;

    video.autoplay = true;
    video.muted = true;
    video.loop = true;

    // Waiting for these 2 events ensures, there is data in the video
    video.addEventListener('playing', function() {
      playing = true;
      checkReady();
    }, true);

    video.addEventListener('timeupdate', function() {
      timeupdate = true;
      checkReady();
    }, true);

    video.src = url;
    video.play();

    function checkReady() {
      if (playing && timeupdate) {
        copyVideo = true;
      }
    }

    return video;
  };

  const createImage = (src) => {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => {
        resolve(img);
      };

      img.onerror = (e) => {
        reject(e);
      };
      img.crossOrigin = 'crossOrigin';
      img.src = src;
    });
  };

  const imageTexture = (gl, image) => {
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    return texture;
  };

  const drawTexture = (gl, program, texture) => {
    // 绘制前，清理canvas
    gl.clearColor(0.0, 0.0, 0.0, 1.0); // 指定颜色缓冲区的清除值
    gl.clearDepth(1.0); // 指定深度缓冲区的清除值
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // 设置顶点坐标属性
    const vertexPostion = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0];
    setAttribute(gl, program, vertexPostion, "a_position");

    // 设置纹理坐标属性
    const txtCoordData = [0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0];
    setAttribute(gl, program, txtCoordData, "a_texCoord");

    /* ***** 纹理设置 ***** */
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    const sampler = gl.getUniformLocation(program, "u_image");
    gl.uniform1i(sampler, 0);

    // gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  };

  const playVideoInWebGL = async () => {
    /* *** 环境准备 *** */

    // 获得webgl上下文
    const gl = createContext("container", 300, 300);
    if (!gl) return;
    gl.clearColor(0, 0, 0, 0);

    // 分别创建顶点着色器和片段着色器
    const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertextSource);
    if (!vertexShader) return;
    const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
    if (!fragmentShader) return;

    // 创建应用程序
    const program = createProgram(gl, [vertexShader, fragmentShader]);
    if (!program) return;

    /* *** 准备纹理数据 *** */
    // 下载视频，并确保视频有可用数据
    const video = setupVideo('./demo.mp4'); // 不阻塞绘制
    // 下载图片，作为初始纹理
    const image = await createImage('cover.png'); // 阻塞绘制
    const texture = imageTexture(gl, image);

    /* *** 播放动画 *** */
    const animation = () => {
      if (copyVideo) { // 拿到视频数据后，更新纹理图片
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
      }
      drawTexture(gl, program, texture); // 重新绘制纹理
      requestAnimationFrame(animation);
    };

    animation();
  };

</script>
</body>
</html>
